package tests.jdl.synchronization;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import jdl.databaseObjects.DbField;
import jdl.databaseObjects.DbObjectState;
import jdl.databaseObjects.FieldCollection;
import jdl.databaseObjects.ObjectAlreadyExistsException;
import jdl.databaseObjects.Table;
import jdl.databaseObjects.datatypes.sqlite.SqliteDbTypeFactory;
import jdl.synchronization.DbObjectNotFoundException;
import jdl.synchronization.SqliteSynchronizer;
import jdl.synchronization.SynchronizationException;
import junit.framework.Assert;

import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class SqliteSynchronizer_Test {
	private SqliteSynchronizer synchronizer;
	private String dbFileName = "tests\\test.db";
	private String testFolderName = "tests";

	@Before
	public void setUp() throws IOException, ClassNotFoundException,
			SQLException {
		deleteTestFolder();
		File testFolder = new File(testFolderName);
		testFolder.mkdir();
		createNewDatabase();
		Class.forName("org.sqlite.JDBC");
		Connection connection = DriverManager.getConnection(String.format(
				"jdbc:sqlite:%s", dbFileName));
		synchronizer = new SqliteSynchronizer(connection);
	}

	@After
	public void tearDown() throws IOException, SQLException {
		synchronizer.dispose();
		deleteTestFolder();
	}

	@Test
	public void synchronizeTable() throws ClassNotFoundException, SQLException,
			SynchronizationException {
		Connection connection = null;
		try {
			connection = getConnection();
			connection.createStatement().execute(
					"CREATE TABLE helloTable (id integer);");
		} finally {
			connection.close();
		}
		Table table = new Table("helloTable");
		synchronizer.synchronizeTable(table);
		Assert.assertEquals(DbObjectState.UNCHANGED, table.getState());
		FieldCollection fieldCollection = table.getFieldCollection();
		Assert.assertEquals(1, fieldCollection.getCount());
		DbField field = fieldCollection.getObjectByIndex(0);
		Assert.assertEquals("id", field.getName());
		Assert.assertEquals(SqliteDbTypeFactory.createIntegerDbType(),
				field.getDbType());
	}

	@Test(expected = DbObjectNotFoundException.class)
	public void synchronizeTable_TableDoesNotExist()
			throws SynchronizationException, ObjectAlreadyExistsException {
		Table table = new Table("abc");
		synchronizer.synchronizeTable(table);
	}

	@Test
	public void synchronizeFieldCollection_CollectionIsEmpty_TableHasOneField()
			throws SQLException, SynchronizationException,
			ClassNotFoundException {
		Connection connection = null;
		try {
			connection = getConnection();
			connection.createStatement().execute(
					"CREATE TABLE charpTable (yahoo integer);");
		} finally {
			connection.close();
		}
		Table table = new Table("charpTable");
		FieldCollection fieldCollection = table.getFieldCollection();
		synchronizer.synchronizeFieldCollection(fieldCollection);
		Assert.assertEquals(1, fieldCollection.getCount());
		DbField field = fieldCollection.getObjectByIndex(0);
		Assert.assertEquals("yahoo", field.getName());
		Assert.assertEquals(SqliteDbTypeFactory.createIntegerDbType(),
				field.getDbType());
		Assert.assertEquals(fieldCollection, field.getFieldCollection());
	}

	@Test
	public void synchronizeFieldCollection_CollectionIsEmpty_TableHasTwoFields()
			throws SQLException, SynchronizationException,
			ClassNotFoundException {
		Connection connection = null;
		try {
			connection = getConnection();
			connection
					.createStatement()
					.execute(
							"CREATE TABLE msft (firstField integer, secondField varchar(50));");
		} finally {
			connection.close();
		}
		Table table = new Table("msft");
		FieldCollection fieldCollection = table.getFieldCollection();
		synchronizer.synchronizeFieldCollection(fieldCollection);
		Assert.assertEquals(2, fieldCollection.getCount());
		DbField firstField = fieldCollection.getFieldByName("firstField");
		Assert.assertNotNull(firstField);
		Assert.assertEquals(SqliteDbTypeFactory.createIntegerDbType(),
				firstField.getDbType());
		Assert.assertEquals(fieldCollection, firstField.getFieldCollection());

		DbField secondField = fieldCollection.getFieldByName("secondField");
		Assert.assertNotNull(secondField);
		Assert.assertEquals(SqliteDbTypeFactory.createVarcharDbType(50),
				secondField.getDbType());
		Assert.assertEquals(fieldCollection, secondField.getFieldCollection());
	}

	@Test
	public void synchronizeField_checkDbTypeOfField() throws Exception {
		Connection connection = null;
		try {
			connection = getConnection();
			connection
					.createStatement()
					.execute(
							"CREATE TABLE qwerty (firstField integer, secondField int);");
		} finally {
			connection.close();
		}
		Table table = new Table("qwerty");
		DbField field = new DbField("secondField",
				SqliteDbTypeFactory.createVarcharDbType(50));
		table.addField(field);
		synchronizer.synchronizeField(field);
		Assert.assertEquals(SqliteDbTypeFactory.createIntDbType(),
				field.getDbType());
	}

	@Test(expected = DbObjectNotFoundException.class)
	public void synchronizeField_() throws Exception {
		Connection connection = null;
		try {
			connection = getConnection();
			connection.createStatement().execute(
					"CREATE TABLE qwerty (firstField integer);");
		} finally {
			connection.close();
		}
		Table table = new Table("qwerty");
		DbField field = new DbField("hello",
				SqliteDbTypeFactory.createVarcharDbType(50));
		table.addField(field);
		synchronizer.synchronizeField(field);
	}

	private Connection getConnection() throws ClassNotFoundException,
			SQLException {
		Class.forName("org.sqlite.JDBC");
		return DriverManager.getConnection(String.format("jdbc:sqlite:%s",
				dbFileName));
	}

	private void createNewDatabase() throws IOException {
		File dbFile = new File(dbFileName);
		dbFile.createNewFile();
	}

	private void deleteTestFolder() throws IOException {
		FileUtils.deleteDirectory(new File(testFolderName));
	}
}
